package com.springboot_blog.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.springboot_blog.dto.LoginDTO;
import com.springboot_blog.dto.RegisterDTO;
import com.springboot_blog.entity.Role;
import com.springboot_blog.entity.User;
import com.springboot_blog.enums.ErrorCodeEnum;
import com.springboot_blog.exception.CustomException;
import com.springboot_blog.mapper.RoleMapper;
import com.springboot_blog.mapper.UserMapper;
import com.springboot_blog.service.AuthService;
import com.springboot_blog.utils.JwtUtil;
import com.springboot_blog.vo.LoginVO;
import com.springboot_blog.vo.RegisterVO;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 认证服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AuthServiceImpl implements AuthService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private RoleMapper roleMapper;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private JwtUtil jwtUtil;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;


    @Override
    public LoginVO login(LoginDTO loginDTO) {
        log.info("用户登录请求: {}, 来源: {}", loginDTO.getUsername(), loginDTO.getSource());

        // 1. 检查登录频率限制（防暴力破解）
        String loginKey = "login:attempt:" + loginDTO.getUsername();
        Integer attempts = (Integer) redisTemplate.opsForValue().get(loginKey);
        if (attempts != null && attempts >= 5) {
            throw new CustomException(ErrorCodeEnum.LOGIN_ATTEMPTS_EXCEEDED);
        }

        //2.根据用户名查询用户
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername, loginDTO.getUsername());
        queryWrapper.eq(User::getDeleted, 0);
        User user = userMapper.selectOne(queryWrapper);

        if (user == null) {
            //记录失败次数
            recordLoginAttempt(loginKey);
            throw new CustomException(ErrorCodeEnum.LOGIN_ERROR);
        }

        //3.检查用户状态
        if (user.getStatus() == 0) {
            throw new CustomException(ErrorCodeEnum.ACCOUNT_DISABLED);
        }

        //4.验证密码
        if (!passwordEncoder.matches(loginDTO.getPassword(), user.getPasswordHash())) {
            recordLoginAttempt(loginKey);
            throw new CustomException(ErrorCodeEnum.LOGIN_ERROR);
        }

        //5.查询用户角色信息
        Role role = roleMapper.selectById(user.getRoleId());
        if (role == null) {
            throw new CustomException(ErrorCodeEnum.USER_ROLE_ERROR);
        }

        //6.验证请求来源
        // 如果来源是admin，则需要验证角色是否为ADMIN或SUPER_ADMIN
        if ("admin".equals(loginDTO.getSource())) {
            if (!"ADMIN".equals(role.getRoleCode()) && !"SUPER_ADMIN".equals(role.getRoleCode())) {
                throw new CustomException(ErrorCodeEnum.UNAUTHORIZED);
            }
        }

        // 7.生成JWT令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", user.getId());
        claims.put("username", user.getUsername());
        claims.put("roleId", user.getRoleId());
        claims.put("roleCode", role.getRoleCode());
        claims.put("source", loginDTO.getSource());

        String accessToken = jwtUtil.generateToken(user.getUsername(), claims);
        String refreshToken = jwtUtil.generateRefreshToken(user.getUsername());

        //8.将令牌存储到Redis
        String tokenKey = "token:" + user.getId();
        String refreshTokenKey = "refresh_token:" + user.getId();

        // 存储访问令牌（24小时）
        redisTemplate.opsForValue().set(tokenKey, accessToken, 24, TimeUnit.HOURS);
        // 存储刷新令牌（7天）
        redisTemplate.opsForValue().set(refreshTokenKey, refreshToken, 7, TimeUnit.DAYS);

        // 9. 存储用户会话信息
        String sessionKey = "session:" + user.getId();
        Map<String, Object> sessionInfo = new HashMap<>();
        sessionInfo.put("userId", user.getId());
        sessionInfo.put("username", user.getUsername());
        sessionInfo.put("roleCode", role.getRoleCode());
        sessionInfo.put("loginTime", LocalDateTime.now());
        sessionInfo.put("source", loginDTO.getSource());

        redisTemplate.opsForHash().putAll(sessionKey, sessionInfo);
        redisTemplate.expire(sessionKey, 24, TimeUnit.HOURS);

        // 10. 更新最后登录时间
        user.setLastLoginTime(LocalDateTime.now());
        userMapper.updateById(user);

        // 11. 清除登录失败记录
        redisTemplate.delete(loginKey);

        // 12. 记录登录日志到Redis
        String loginLogKey = "login:log:" + user.getId() + ":" + System.currentTimeMillis();
        Map<String, Object> loginLog = new HashMap<>();
        loginLog.put("userId", user.getId());
        loginLog.put("username", user.getUsername());
        loginLog.put("loginTime", LocalDateTime.now());
        loginLog.put("source", loginDTO.getSource());
        loginLog.put("rememberMe", loginDTO.getRememberMe());

        redisTemplate.opsForHash().putAll(loginLogKey, loginLog);
        redisTemplate.expire(loginLogKey, 30, TimeUnit.DAYS);

        // 12. 构建返回结果
        LoginVO.UserInfo userInfo = LoginVO.UserInfo.builder()
                .id(user.getId())
                .username(user.getUsername())
                .nickname(user.getNickname())
                .email(user.getEmail())
                .avatar(user.getAvatar())
                .roleId(role.getId())
                .roleName(role.getRoleName())
                .roleCode(role.getRoleCode())
                .lastLoginTime(user.getLastLoginTime())
                .build();

        Long expiresIn = loginDTO.getRememberMe() ? 7 * 24 * 3600L : 24 * 3600L; // 记住我7天，否则24小时

        LoginVO loginVO = LoginVO.builder()
                .accessToken(accessToken)
                .refreshToken(refreshToken)
                .tokenType("Bearer")
                .expiresIn(expiresIn)
                .userInfo(userInfo)
                .build();

        log.info("用户 {} 登录成功，来源: {}", user.getUsername(), loginDTO.getSource());
        return loginVO;

    }

    @Override
    public RegisterVO register(RegisterDTO registerDTO) {
        log.info("开始用户注册，用户名: {}, 邮箱: {}", registerDTO.getUsername(), registerDTO.getEmail());

        //1. 验证密码是否一致
        if (!registerDTO.getPassword().equals(registerDTO.getConfirmPassword())) {
            throw new CustomException(ErrorCodeEnum.PASSWORD_NOT_MATCH);
        }

        //2. 验证用户名是否已存在
        if (existsByUsername(registerDTO.getUsername())) {
            throw new CustomException(ErrorCodeEnum.USER_ALREADY_EXISTS);
        }

        //3. 验证邮箱是否已存在
        if (existsByEmail(registerDTO.getEmail())) {
            throw new CustomException(ErrorCodeEnum.EMAIL_ALREADY_EXISTS);
        }

        //4. 创建用户

        User user = new User();
        user.setUsername(registerDTO.getUsername());
        user.setPasswordHash(passwordEncoder.encode(registerDTO.getPassword()));
        user.setNickname(registerDTO.getNickname());
        user.setEmail(registerDTO.getEmail());
        user.setRoleId(3L);
        user.setStatus(1); // 正常状态
        user.setAvatar(""); // 设置默认头像
        user.setCreatedAt(LocalDateTime.now());
        user.setUpdatedAt(LocalDateTime.now());

        //5. 保存用户
        int result = userMapper.insert(user);
        if (result <= 0) {
            throw new CustomException("注册失败，请重试");
        }

        log.info("用户注册成功，用户ID: {}, 用户名: {}", user.getId(), user.getUsername());

        return RegisterVO.builder()
                .userId(user.getId())
                .username(user.getUsername())
                .nickname(user.getNickname())
                .email(user.getEmail())
                .avatar(user.getAvatar())
                .build();

    }

    /**
     * 记录登录失败次数
     */
    private void recordLoginAttempt(String loginKey) {
        Integer attempts = (Integer) redisTemplate.opsForValue().get(loginKey);
        if (attempts == null) {
            attempts = 0;
        }
        attempts++;
        redisTemplate.opsForValue().set(loginKey, attempts, 30, TimeUnit.MINUTES);
    }

    /**
     * 检查用户名是否存在
     */
    private boolean existsByUsername(String username) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername, username);
        return userMapper.selectCount(queryWrapper) > 0;
    }

    /**
     * 检查邮箱是否存在
     */
    private boolean existsByEmail(String email) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getEmail, email);
        return userMapper.selectCount(queryWrapper) > 0;
    }


}
